#include "GSharpFieldProperty.h"
#include "GSharpPropertyList.h"
#include "FieldValuesContainer.h"
#include "Object/Object.h"
#include "ScriptContent.h"

#include "GSharpStruct.h"
#include "GSharpClass.h"
#include "GSharpEnum.h"

//get value for non static struct's field
class CStructNonStaticFieldGetSet : public INonStaticProperty
{
public:
    CStructNonStaticFieldGetSet(CGSharpFieldProperty* InFieldProperty )
    {
        FieldProperty = InFieldProperty;
    }

    virtual CAny GetValue(const void * InObjectPtr) override
    {
        const CGSharpFieldValuesContainer* Container 
            = reinterpret_cast<const CGSharpFieldValuesContainer*>(InObjectPtr);

        return Container->GetValue(FieldProperty->GetFieldIndex());
    }
	virtual void SetValue(void * InObjectPtr,const CAny& InValue) override
    {
        CGSharpFieldValuesContainer* Container 
            = reinterpret_cast<CGSharpFieldValuesContainer*>(InObjectPtr);

        Container->SetValue(
            FieldProperty->GetFieldIndex()
            ,InValue
            );
    }

protected:
    CGSharpFieldProperty * FieldProperty;

};

//get/set value for static struct's field
class CStructStaticFieldGetSet: public IStaticProperty
{
public:
    CStructStaticFieldGetSet(CGSharpFieldProperty* InFieldProperty )
    {
        FieldProperty = InFieldProperty;
    }

    virtual CAny GetValue() override
    {
        //convert type to struct
        CGSharpStruct* StructType = reinterpret_cast<CGSharpStruct*>(FieldProperty->GetOwnerType());
        return StructType->GetStaticValues()->GetValue(FieldProperty->GetFieldIndex());
    }

    virtual void SetValue(const CAny& InValue) override
    {
        //convert type to struct
        CGSharpStruct* StructType = reinterpret_cast<CGSharpStruct*>(FieldProperty->GetOwnerType());
        StructType->GetStaticValues()->SetValue(FieldProperty->GetFieldIndex(),InValue);
    }

protected:
    CGSharpFieldProperty * FieldProperty;

};

class CStructStaticFieldGetPointer : public IFieldProperty
{
public:
    CStructStaticFieldGetPointer(CGSharpFieldProperty* InFieldProperty )
    {
        FieldProperty = InFieldProperty;
    }

    const void* GetValuePointer(const void* InObjectPtr)
    {
        return GetStaticValuePointer();
    }
	void* GetValuePointer( void* InObjectPtr)
    {
        return GetStaticValuePointer();
    }

    uint32_t GetFieldIndex()
    {
        return FieldProperty->GetFieldIndex();
    }

    bool IsStatic() override
    {
        return true;
    }

    void* GetStaticValuePointer() override
    {
        //convert type to struct
        CGSharpStruct* StructType = reinterpret_cast<CGSharpStruct*>(FieldProperty->GetOwnerType());
        return StructType->GetStaticValues()->GetValuePointer(FieldProperty->GetFieldIndex());
    }


protected:
    CGSharpFieldProperty * FieldProperty;

};

class CStructNonStaticFieldGetPointer : public IFieldProperty
{
public:
    CStructNonStaticFieldGetPointer(CGSharpFieldProperty* InFieldProperty )
    {
        FieldProperty = InFieldProperty;
    }

    const void* GetValuePointer(const void* InObjectPtr)
    {
        const CGSharpFieldValuesContainer* Container 
            = reinterpret_cast<const CGSharpFieldValuesContainer*>(InObjectPtr);

        return Container->GetValue(FieldProperty->GetFieldIndex()).GetPointer();
    }
    void* GetValuePointer( void* InObjectPtr)
    {
        CGSharpFieldValuesContainer* Container 
            = reinterpret_cast<CGSharpFieldValuesContainer*>(InObjectPtr);

        return Container->GetValue(FieldProperty->GetFieldIndex()).GetPointer();
    }

    uint32_t GetFieldIndex()
    {
        return FieldProperty->GetFieldIndex();
    }

    bool IsStatic() override
    {
        return false;
    }

    void* GetStaticValuePointer() override
    {
        assert(false);
        return nullptr;
    }

protected:
    CGSharpFieldProperty * FieldProperty;
};


class CEnumStaticFieldGetSet: public IStaticProperty
{
public:
    CEnumStaticFieldGetSet(CGSharpFieldProperty* InFieldProperty )
    {
        FieldProperty = InFieldProperty;
    }

    virtual CAny GetValue() override
    {
        //convert type to struct
        CGSharpEnum* EnumType = reinterpret_cast<CGSharpEnum*>(FieldProperty->GetOwnerType());
        return EnumType->GetStaticValues()->GetValue(FieldProperty->GetFieldIndex());
    }

    virtual void SetValue(const CAny& InValue) override
    {
        //convert type to struct
        CGSharpEnum* EnumType = reinterpret_cast<CGSharpEnum*>(FieldProperty->GetOwnerType());
        EnumType->GetStaticValues()->SetValue(FieldProperty->GetFieldIndex(),InValue);
    }

protected:
    CGSharpFieldProperty * FieldProperty;

};


class CEnumStaticFieldGetPointer : public IFieldProperty
{
public:
    CEnumStaticFieldGetPointer(CGSharpFieldProperty* InFieldProperty )
    {
        FieldProperty = InFieldProperty;
    }

    const void* GetValuePointer(const void* InObjectPtr)
    {
        return GetStaticValuePointer();
    }
    void* GetValuePointer( void* InObjectPtr)
    {
        return GetStaticValuePointer();
    }

    uint32_t GetFieldIndex()
    {
        return FieldProperty->GetFieldIndex();
    }

    bool IsStatic() override
    {
        return true;
    }

    void* GetStaticValuePointer() override
    {
        //convert type to struct
        CGSharpEnum* EnumType = reinterpret_cast<CGSharpEnum*>(FieldProperty->GetOwnerType());
        return EnumType->GetStaticValues()->GetValuePointer(FieldProperty->GetFieldIndex());
    }

protected:
    CGSharpFieldProperty * FieldProperty;

};


class CClassNonStaticFieldGetSet: public INonStaticProperty
{
public:
    CClassNonStaticFieldGetSet(CGSharpFieldProperty* InFieldProperty )
    {
        FieldProperty = InFieldProperty;
    }

    virtual CAny GetValue(const void * InObjectPtr) override
    {
        //cast to RObject
        const RObject* Object = reinterpret_cast<const RObject*>(InObjectPtr);

        return Object->GetScriptContent()
            ->GetNonStaticFieldValues()
            ->GetValue(FieldProperty->GetFieldIndex());
    }

    virtual void SetValue(void * InObjectPtr,const CAny& InValue) override
    {
        //cast to RObject
        RObject* Object = reinterpret_cast<RObject*>(InObjectPtr);

        Object->GetScriptContent()
            ->GetNonStaticFieldValues()
            ->SetValue(FieldProperty->GetFieldIndex(),InValue);
    }

protected:
    CGSharpFieldProperty * FieldProperty;

};


class CClassStaticFieldGetSet: public IStaticProperty
{
public:
    CClassStaticFieldGetSet(CGSharpFieldProperty* InFieldProperty )
    {
        FieldProperty = InFieldProperty;
    }

    virtual CAny GetValue() override
    {
        //convert type to struct
        CGSharpClass* Class = reinterpret_cast<CGSharpClass*>(FieldProperty->GetOwnerType());
        return Class->GetStaticValues()->GetValue(FieldProperty->GetFieldIndex());
    }

    virtual void SetValue(const CAny& InValue) override
    {
        //convert type to struct
        CGSharpClass* Class = reinterpret_cast<CGSharpClass*>(FieldProperty->GetOwnerType());
        Class->GetStaticValues()->SetValue(FieldProperty->GetFieldIndex(),InValue);
    }  

protected:
    CGSharpFieldProperty * FieldProperty;

};

class CClassStaticFieldGetPointer : public IFieldProperty
{
public:
    CClassStaticFieldGetPointer(CGSharpFieldProperty* InFieldProperty )
    {
        FieldProperty = InFieldProperty;
    }

    const void* GetValuePointer(const void* InObjectPtr)
    {
        return GetStaticValuePointer();
    }
    void* GetValuePointer( void* InObjectPtr)
    {
        return GetStaticValuePointer();
    }

    uint32_t GetFieldIndex()
    {
        return FieldProperty->GetFieldIndex();
    }

    virtual bool IsStatic() override
    {
        return true;
    }

    virtual void* GetStaticValuePointer() override
    {
        //convert type to struct
        CGSharpClass* Class = reinterpret_cast<CGSharpClass*>(FieldProperty->GetOwnerType());
        return Class->GetStaticValues()->GetValuePointer(FieldProperty->GetFieldIndex());
    }

protected:
    CGSharpFieldProperty * FieldProperty;

};

class CClassNonStaticFieldGetPointer : public IFieldProperty
{
public:
    CClassNonStaticFieldGetPointer(CGSharpFieldProperty* InFieldProperty )
    {
        FieldProperty = InFieldProperty;
    }

    const void* GetValuePointer(const void* InObjectPtr)
    {
        const RObject* Object = reinterpret_cast<const RObject*>(InObjectPtr);
        return Object->GetScriptContent()
            ->GetNonStaticFieldValues()
            ->GetValuePointer(FieldProperty->GetFieldIndex())
            ;
    }
    void* GetValuePointer( void* InObjectPtr)
    {
        const RObject* Object = reinterpret_cast<const RObject*>(InObjectPtr);
        return Object->GetScriptContent()
            ->GetNonStaticFieldValues()
            ->GetValuePointer(FieldProperty->GetFieldIndex())
            ;
    }

    uint32_t GetFieldIndex()
    {
        return FieldProperty->GetFieldIndex();
    }

    virtual bool IsStatic() override
    {
        return false;
    }

    virtual void* GetStaticValuePointer() override
    {
        assert(false);
        return nullptr;
    }

protected:
    CGSharpFieldProperty * FieldProperty;

};




CGSharpFieldProperty::CGSharpFieldProperty(
        CGSharpPropertyList* InParentList
        ,CString InName
		,const SDecoratedType & InType
        ,TMap<EPropertyFlags,  SReflectionMetaDataValue> InMetaData
		,bool InIsStatic
		,uint64_t InFieldIndex
        ,bool InIsStruct
        ,bool InIsEnum
        ,TOptional<CConstantValue> InDefaultValue
    )
    :CProperty(
        InParentList
        , InName    
        , InType
        , InMetaData
        )
    ,FieldIndex(InFieldIndex)
    ,DefaultValue(InDefaultValue)
{

    //make sure the DefaultValue's type is same as the field type
    if(DefaultValue.HasValue())
    {
        assert(DefaultValue.GetValue().GetType() == InType.BasicType);
    }

    if (InIsStatic)
    {
        if(InIsStruct)
        {
            StaticProperty = new CStructStaticFieldGetSet(this);
            FieldProperty = new CStructStaticFieldGetPointer(this);
        }
        else if(InIsEnum)
        {
            StaticProperty = new CEnumStaticFieldGetSet(this);
            FieldProperty = new CEnumStaticFieldGetPointer(this);
        }
        else
        {
            StaticProperty = new CClassStaticFieldGetSet(this);
            FieldProperty = new CClassStaticFieldGetPointer(this);
        }
    }
    else
    {
        assert(!InIsEnum &&" do not support field of enum type");
        if(InIsStruct)
        {
            NonStaticProperty = new CStructNonStaticFieldGetSet(this);
            FieldProperty = new CStructNonStaticFieldGetPointer(this);
        }
        else
        {
            NonStaticProperty = new CClassNonStaticFieldGetSet(this);
            FieldProperty = new CClassNonStaticFieldGetPointer(this);
        }        
    }

    ScriptProperty= new IScriptProperty();
}
